---
title: Popover
description: Using the popover machine in your project.
package: "@zag-js/popover"
---

A popover is a non-modal dialog that floats around a trigger. It is used to
display contextual information to the user, and should be paired with a
clickable trigger element.

<Resources pkg="@zag-js/popover" />

<Showcase id="Popover" />

**Features**

- Focus is managed and can be customized
- Supports modal and non-modal modes
- Ensures correct DOM order after tabbing out of the popover, whether it's
  portalled or not

## Installation

To use the popover machine in your project, run the following command in your
command line:

<CodeSnippet id="popover/installation.mdx" />

## Anatomy

To set up the popover correctly, you'll need to understand its anatomy and how
we name its parts.

> Each part includes a `data-part` attribute to help identify them in the DOM.

<Anatomy id="popover" />

## Usage

First, import the popover package into your project

```jsx
import * as popover from "@zag-js/popover"
```

The popover package exports two key functions:

- `machine` — The state machine logic for the popover widget.
- `connect` — The function that translates the machine's state to JSX attributes
  and event handlers.

> You'll need to provide a unique `id` to the `useMachine` hook. This is used to
> ensure that every part has a unique identifier.

Next, import the required hooks and functions for your framework and use the
popover machine in your project 🔥

<CodeSnippet id="popover/usage.mdx" />

### Rendering the popover in a portal

By default, the popover is rendered in the same DOM hierarchy as the trigger. To
render the popover within a portal, pass `portalled: true` property to the
machine's context.

> Note: This requires that you render the component within a `Portal` based on
> the framework you use.

<CodeSnippet id="popover/render-in-portal.mdx" />

### Managing focus within popover

When the popover open, focus is automatically set to the first focusable element
within the popover. To customize the element that should get focus, set the
`initialFocusEl` property in the machine's context.

<CodeSnippet id="popover/initial-focus.mdx" />

### Changing the modality

In some cases, you might want the popover to be **modal**. This means that
it'll:

- trap focus within its content
- block scrolling on the `body`
- disable pointer interactions outside the popover
- hide content behind the popover from screen readers

To make the popover modal, set the `modal: true` property in the machine's
context. When `modal: true`, we set the `portalled` attribute to `true` as well.

> **Note**: This requires that you render the component within a `Portal`.

```jsx {2}
const service = useMachine(popover.machine, {
  modal: true,
})
```

### Close behavior

The popover is designed to close on blur and when the `esc` key is pressed.

To prevent it from closing on blur (clicking or focusing outside), pass the
`closeOnInteractOutside` property and set it to `false`.

```jsx {2}
const service = useMachine(popover.machine, {
  closeOnInteractOutside: true,
})
```

To prevent it from closing when the `esc` key is pressed, pass the
`closeOnEscape` property and set it to `false`.

```jsx {2}
const service = useMachine(popover.machine, {
  closeOnEscape: true,
})
```

### Adding an arrow

To render an arrow within the popover, use the `api.getArrowProps()` and
`api.getArrowTipProps()`.

```jsx {6-8}
//...
const api = popover.connect(service, normalizeProps)
//...
return (
  <div {...api.getPositionerProps()}>
    <div {...api.getContentProps()}>
      <div {...api.getArrowProps()}>
        <div {...api.getArrowTipProps()} />
      </div>
      //...
    </div>
  </div>
)
//...
```

### Changing the placement

To change the placement of the popover, set the `positioning.placement` property
in the machine's context.

```jsx {2-4}
const service = useMachine(popover.machine, {
  positioning: {
    placement: "top-start",
  },
})
```

### Listening for open state changes

When the popover is opened or closed, the `onOpenChange` callback is invoked.

```jsx {2-7}
const service = useMachine(popover.machine, {
  onOpenChange(details) {
    // details => { open: boolean }
    console.log("Popover", details.open)
  },
})
```

### Usage within dialog

When using the popover within a dialog, avoid rendering the popover in a
`Portal` or `Teleport`. This is because the dialog will trap focus within it,
and the popover will be rendered outside the dialog.

## Styling guide

Earlier, we mentioned that each popover part has a `data-part` attribute added
to them to select and style them in the DOM.

### Open and closed state

When the popover is expanded, we add a `data-state` and `data-placement`
attribute to the trigger.

```css
[data-part="trigger"][data-state="open|closed"] {
  /* styles for the expanded state */
}

[data-part="content"][data-state="open|closed"] {
  /* styles for the expanded state */
}

[data-part="trigger"][data-placement="(top|bottom)-(start|end)"] {
  /* styles for computed placement */
}
```

### Position aware

When the popover is expanded, we add a `data-state` and `data-placement`
attribute to the trigger.

```css
[data-part="trigger"][data-placement="(top|bottom)-(start|end)"] {
  /* styles for computed placement */
}

[data-part="content"][data-placement="(top|bottom)-(start|end)"] {
  /* styles for computed placement */
}
```

### Arrow

The arrow element requires specific css variables to be set for it to show
correctly.

```css
[data-part="arrow"] {
  --arrow-background: white;
  --arrow-size: 16px;
}
```

A common technique for adding a shadow to the arrow is to use set
`filter: drop-down(...)` css property on the content element. Alternatively, you
can use the `--arrow-shadow-color` variable.

```css
[data-part="arrow"] {
  --arrow-shadow-color: gray;
}
```

## Methods and Properties

### Machine Context

The popover machine exposes the following context properties:

<ContextTable name="popover" />

### Machine API

The popover `api` exposes the following methods:

<ApiTable name="popover" />

### Data Attributes

<DataAttrTable name="popover" />

### CSS Variables

<CssVarTable name="popover" />

## Accessibility

Adheres to the
[Dialog WAI-ARIA design pattern](https://www.w3.org/WAI/ARIA/apg/patterns/dialogmodal).

### Keyboard Interactions

<KeyboardTable name="popover" />
